package top.ibase4j.core.config;

import java.io.Serializable;
import java.time.Duration;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;

import redis.clients.jedis.JedisPoolConfig;
import top.ibase4j.core.support.cache.RedisHelper;
import top.ibase4j.core.support.jedis.ConnectionFactory;
import top.ibase4j.core.util.CacheUtil;
import top.ibase4j.core.util.DataUtil;
import top.ibase4j.core.util.InstanceUtil;
import top.ibase4j.core.util.PropertiesUtil;

@Configuration
@ConditionalOnClass(JedisPoolConfig.class)
public class JedisConfig {
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMinIdle(PropertiesUtil.getInt("redis.minIdle"));
        poolConfig.setMaxIdle(PropertiesUtil.getInt("redis.maxIdle"));
        poolConfig.setMaxTotal(PropertiesUtil.getInt("redis.maxTotal"));
        poolConfig.setMaxWaitMillis(PropertiesUtil.getInt("redis.maxWaitMillis"));
        poolConfig.setTestOnBorrow(Boolean.valueOf(PropertiesUtil.getString("redis.testOnBorrow")));
        return poolConfig;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
        JedisConnectionFactory jedisConnectionFactory;
        String nodes = PropertiesUtil.getString("redis.cluster.nodes");
        String master = PropertiesUtil.getString("redis.master");
        String sentinels = PropertiesUtil.getString("redis.sentinels");
        JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();
        Duration connectTimeout = Duration.ofSeconds(PropertiesUtil.getInt("redis.connectTimeout"));
        Duration readTimeout = Duration.ofSeconds(PropertiesUtil.getInt("redis.readTimeout"));
        JedisClientConfiguration clientConfiguration = builder.connectTimeout(connectTimeout).readTimeout(readTimeout)
            .usePooling().poolConfig(jedisPoolConfig).build();
        RedisPassword password = RedisPassword.of(PropertiesUtil.getString("redis.password"));
        String host = PropertiesUtil.getString("redis.host");
        String port = PropertiesUtil.getString("redis.port");
        if (DataUtil.isNotEmpty(nodes)) {
            List<String> list = InstanceUtil.newArrayList(nodes.split(","));
            RedisClusterConfiguration configuration = new RedisClusterConfiguration(list);
            configuration.setMaxRedirects(PropertiesUtil.getInt("redis.cluster.max-redirects"));
            configuration.setPassword(password);
            jedisConnectionFactory = new JedisConnectionFactory(configuration, clientConfiguration);
        } else if (DataUtil.isNotEmpty(master) && DataUtil.isNotEmpty(sentinels)) {
            Set<String> set = InstanceUtil.newHashSet(sentinels.split(","));
            RedisSentinelConfiguration configuration = new RedisSentinelConfiguration(master, set);
            configuration.setPassword(password);
            jedisConnectionFactory = new JedisConnectionFactory(configuration, clientConfiguration);
        } else if (DataUtil.isNotEmpty(host) && DataUtil.isNotEmpty(port)) {
            RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
            configuration.setPassword(password);
            configuration.setHostName(host);
            configuration.setPort(Integer.valueOf(port));
            jedisConnectionFactory = new JedisConnectionFactory(configuration, clientConfiguration);
        } else {
            throw new RuntimeException("redis 配置错误");
        }
        return jedisConnectionFactory;
    }

    @Bean
    public RedisTemplate<?, ?> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate<Serializable, Serializable> redisTemplate = new RedisTemplate<Serializable, Serializable>();
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        GenericFastJsonRedisSerializer valueSerializer = new GenericFastJsonRedisSerializer();
        redisTemplate.setEnableTransactionSupport(new Boolean(PropertiesUtil.getString("redis.enableTransaction")));
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setKeySerializer(keySerializer);
        redisTemplate.setValueSerializer(valueSerializer);
        redisTemplate.setHashKeySerializer(keySerializer);
        redisTemplate.setHashValueSerializer(valueSerializer);
        return redisTemplate;
    }

    @Bean
    @Qualifier("redisTemplate")
    public RedisHelper redisHelper(RedisTemplate<Serializable, Serializable> redisTemplate) {
        RedisHelper redisHelper = new RedisHelper();
        redisHelper.setRedisTemplate(redisTemplate);
        return redisHelper;
    }

    @Bean
    public CacheManager redisCacheManager(JedisConnectionFactory jedisConnectionFactory) {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofSeconds(PropertiesUtil.getInt("redis.cache.ttl", 10)));
        RedisCacheManagerBuilder builder = RedisCacheManager.builder(jedisConnectionFactory)
            .cacheDefaults(configuration);
        if (new Boolean(PropertiesUtil.getString("redis.cache.enableTransaction"))) {
            builder.transactionAware();
        }
        RedisCacheManager cacheManager = builder.build();
        return cacheManager;
    }

    @Bean
    @ConditionalOnMissingClass("org.redisson.api.RedissonClient")
    @ConditionalOnMissingBean(type = {"org.redisson.api.RedissonClient"})
    public ConnectionFactory redisConnectFactory(JedisPoolConfig poolConfig) {
        ConnectionFactory connectionFactory = new ConnectionFactory(poolConfig);
        String sharded = PropertiesUtil.getString("redis.sharded.nodes");
        String cluster = PropertiesUtil.getString("redis.cluster.nodes");
        String maxRedirects = PropertiesUtil.getString("redis.cluster.max-redirects");

        String master = PropertiesUtil.getString("redis.master");
        String sentinels = PropertiesUtil.getString("redis.sentinels");

        String password = PropertiesUtil.getString("redis.password");
        connectionFactory.setPassword(password);
        String host = PropertiesUtil.getString("redis.host");
        String port = PropertiesUtil.getString("redis.port");
        if (DataUtil.isNotEmpty(cluster)) {
            connectionFactory.setClusterAddress(cluster);
            if (DataUtil.isNotEmpty(maxRedirects)) {
                connectionFactory.setMaxRedirects(Integer.valueOf(maxRedirects));
            }
        } else if (DataUtil.isNotEmpty(sharded)) {
            connectionFactory.setShardedAddress(sharded);
        } else if (DataUtil.isNotEmpty(master) && DataUtil.isNotEmpty(sentinels)) {
            connectionFactory.setMaster(master);
            connectionFactory.setSentinels(sentinels);
        } else if (DataUtil.isNotEmpty(host) && DataUtil.isNotEmpty(port)) {
            connectionFactory.setHost(host);
            connectionFactory.setPort(Integer.valueOf(port));
        } else {
            throw new RuntimeException("redis 配置错误");
        }
        String connectTimeout = PropertiesUtil.getString("redis.connectTimeout");
        String readTimeout = PropertiesUtil.getString("redis.readTimeout");
        if (DataUtil.isNotEmpty(connectTimeout)) {
            connectionFactory.setConnectTimeout(Integer.valueOf(connectTimeout));
        }
        if (DataUtil.isNotEmpty(readTimeout)) {
            connectionFactory.setReadTimeout(Integer.valueOf(readTimeout));
        }
        CacheUtil.setLockManager(connectionFactory);
        return connectionFactory;
    }
}
